import { isArray, isString } from "@/utils/is"

/**
 * URL
 * @param {'wss://' | 'ws://'} protocol     // websocket连接protocol
 * @param {string} host                     // websocket连接host
 * @param {string} prefix                   // 接口路径前缀
 * @param {string} path                     // websocket连接path
 */

// 根据配置项得到websocket连接地址
const createURL = (options) => {
    let url = ''
    // 得到websocket连接的protocol
    let protocol = options.urlOption.protocol ? options.urlOption.protocol : window.location.protocol == 'http:' ? 'ws://' : window.location.protocol == 'https:' ? 'wss://' : ""
    // 得到websocket连接的host
    let host = options.urlOption.host ? options.urlOption.host : window.location.host
    // 拼接得到websocket连接地址
    url = protocol + host + (options.urlOption.prefix || '') + options.urlOption.path
    // 如果需要path参数，遍历拼接在路径后面
    if (options.path_parameter) {
        if (isArray(options.path_parameter)) {
            options.path_parameter.forEach(parameter => {
                url = url + `/${parameter}`
            })
        } else if (isString(options.path_parameter)) {
            url = url + `/${options.path_parameter}`
        }
    }
    // 如果需要params参数，遍历拼接在路径后面
    if (options.params_parameter) {
        Object.keys(options.params_parameter).forEach((key, index) => {
            if (index == 0) {
                options.params_parameter && (url = url + `?${key}=${options.params_parameter[key]}`)
            } else {
                options.params_parameter && (url = url + `&${key}=${options.params_parameter[key]}`)
            }
        })
    }
    return url
}

export class CreateWebsocket {
    constructor(id, options) {
        this.id = id
        this.options = options
        this.url = createURL(options)
        this.handlers = {
            open: [],
            close: [],
            error: [],
            message: [],
        };
        this.connectionAttempt = null   // 重连setInterval
        this.isDestory = false   // 是否是组件销毁关闭连接
        this.reconnectNum = 0    // 重连的次数
        this.heartbeatOption = {
            timeout: null,
            interval: null,
        }

        this.connect()
    }
    // 连接websocket
    connect() {
        this.state = 'init'
        try {
            if (window.WebSocket) {
                this.ws = new WebSocket(this.url)
                this.addFunction()
            } else if (window.MozWebSocket) {
                this.ws = new window.MozWebSocket(this.url)
                this.addFunction()
            } else {
                throw Error('当前浏览器不支持websocket')
            }
        } catch (err) {
            console.log(err)
            this.reconnect()
        }
    }
    addFunction() {
        this.ws.onopen = () => {
            this.state = 'open'
            this.invokeHandlers('open')
            if (this.options.openHeartbeat) {
                this.createHeartbeat()
            }
        }
        this.ws.onerror = error => {
            this.state = 'error'
            this.invokeHandlers('error', error)
        }
        this.ws.onclose = data => {
            this.state = 'closed'
            this.invokeHandlers('close', data)
            if (!this.isDestory) {
                this.reconnect()
            }
        }
        this.ws.onmessage = res => {
            const msg = (this.options && this.options.jsonParse) ? JSON.parse(res.data) : res.data
            if (this.state !== 'destroyed' && this.state !== 'closed') {
                this.state = 'open';
            }
            this.invokeHandlers('open', msg)
            // 当有消息返回,说明websocket正常,重置心跳
            this.createHeartbeat()
        }
    }
    // 重连websocket
    reconnect() {
        if (this.connectionAttempt || this.state === 'destroyed' || !this.options.reconnect) return
        const attempt = () => {
            if (this.state === 'open' || this.reconnectNum >= (this.options.maxReconnectNum || 5)) {
                clearInterval(this.connectionAttempt);
                this.connectionAttempt = null;
                this.reconnectNum = 0
                return;
            }
            this.reconnectNum++
            this.connect()
        }
        this.connectionAttempt = setInterval(attempt, this.options.reconnectDelay || 2000);
    }
    // 将不同状态的函数存储在handlers中
    registerHandler(key, fn) {
        if (this.state == 'destroyed') return
        this.handlers[key].push(fn)
    }
    // 在websocket对应类型的函数触发时，执行存储在handlers中相同type的所有函数
    invokeHandlers(type, data) {
        const handlers = this.handlers[type];
        if (!handlers) return
        handlers.forEach((handler) => {
            try {
                handler(data)
            } catch (err) {
                console.log(err)
            }
        })
    }
    // open函数
    onopen(fun) {
        this.registerHandler('open', fun)
        return this
    }
    // close函数
    onclose(fun) {
        this.registerHandler('close', fun)
        return this
    }
    // error函数
    onerror(fun) {
        this.registerHandler('error', fun)
        return this
    }
    // message函数
    onmessage(fun) {
        this.registerHandler('message', fun)
        return this
    }
    // 发送消息给websocket
    onsend(data) {
        this.ws && this.ws.send(data)
    }
    // 销毁事件
    ondestory() {
        this.isDestory = true
        this.state = 'destroyed'
        if (this.ws) {
            this.ws.close()
        }
        clearTimeout(this.heartbeatOption.timeout)
        clearInterval(this.heartbeatOption.interval)
    }
    // 创建心跳
    createHeartbeat() {
        // 清除现有定时器和倒计时重置心跳
        clearTimeout(this.heartbeatOption.timeout)
        clearInterval(this.heartbeatOption.interval)
        // 在连接websocket或接收消息之后,经过heartbeatTime时间像websocket发送一条数据
        this.heartbeatOption.interval = setInterval(() => {
            let data = this.options.heartbeatOptions?.sendData
            this.ws.send(JSON.stringify(data))
            // 如果在closeTime时间内没有收到返回数据,关闭websocket触发重连
            this.heartbeatOption.timeout = setTimeout(() => {
                this.ws.close()
            }, this.options.heartbeatOptions?.closeTime || 2000)
        }, this.options.heartbeatOptions?.heartbeatTime || 30000)
    }
}
